// IPConfig.cpp: implementation of the CIPConfig class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "IPConfig.h"
#include "md5.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

DWORD WINAPI RecvReqThread(LPVOID pParam)
{
	CIPConfig* pP = (CIPConfig*) pParam;
	pP->RecvReq();
	return 0;
}

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CIPConfig::CIPConfig() : m_s(m_socket)
{
	TCHAR szPath[MAX_PATH];
	GetModuleFileName(NULL, szPath, MAX_PATH);
	CString strPath = szPath;
	strPath = strPath.Left(strPath.ReverseFind('\\'));
	strPath = strPath.Left(strPath.ReverseFind('\\'));
	m_strFile = strPath + _T("\\Config\\Network.ini");

	AfxSocketInit();
	
	m_hThread = NULL;
	m_hThreadR = NULL;

	GetBroadcastIP();

	// send broadcast
	m_socket = socket(AF_INET, SOCK_DGRAM, 0);

	// send register key
	m_socketR = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	m_lRegistered = 0;
	m_lThread = 0;
}

CIPConfig::~CIPConfig()
{
	closesocket(m_socket);
	closesocket(m_socketR);

	for (POSITION pos = m_lstServers.GetHeadPosition(); pos != NULL;)
	{
		CString strServer =  m_lstServers.GetNext(pos);
		WritePrivateProfileString(strServer, _T("IPA"), _T("127.0.0.1"), m_strFile);
	}

	if (m_hThread != NULL)
	{
		Sleep(100);
		CloseHandle(m_hThread);
		m_hThread = NULL;
	}

	if (m_hThreadR != NULL)
	{
		Sleep(100);
		CloseHandle(m_hThreadR);
		m_hThreadR = NULL;
	}
}

void CIPConfig::GetBroadcastIP()
{
	char name[200];
	char* tip;
	hostent* hoststruct;
	
	// Get Local Host IP
	gethostname(name, 200);
	hoststruct = gethostbyname(name);
	tip = inet_ntoa(*(struct in_addr *) *hoststruct->h_addr_list);
	
	TCHAR szIP[16];
	strcpy(szIP, tip);
	m_strIP = szIP;
}

void CIPConfig::RecvReq()
{
	SOCKET& s = m_s;

	InterlockedExchange(&m_lThread, 0L);

	SOCKADDR_IN addrFrom;
	int len = sizeof(SOCKADDR);
	
	char recvBuf[4096];
	int retval;
	while (TRUE)
	{
		ZeroMemory(recvBuf, 4096);
		// TRACE(_T("Recv\n"));
		retval = recvfrom(s, recvBuf, 4096, 0, (SOCKADDR *) &addrFrom,
			&len);

		if (SOCKET_ERROR == retval)
		{
			DWORD dwErr = GetLastError();
			break;
		}
		
		CString strRecv = recvBuf;
		if (strRecv.GetLength() > 4)
		{
			TRACE(strRecv);
			TRACE(_T("\n"));
			if (strRecv.Left(3) == _T("IP_"))
			{
				int iFind = strRecv.Find(_T(":"));
				CString strServer = strRecv.Left(iFind);
				strServer = strServer.Right(strServer.GetLength() - 3);
				CString strIP = strRecv.Right(strRecv.GetLength() - iFind - 1);
				WritePrivateProfileString(strServer, _T("IPA"), strIP, m_strFile);
			}
			else if (strRecv.Left(4) == _T("GET_"))
			{
				CString strServer = strRecv.Right(strRecv.GetLength() - 4);
				if (strServer.CompareNoCase(m_strServer) == 0)
				{
					CString strSend;
					strSend.Format(_T("IP_%s:%s"), m_strServer, m_strIP);
					sendto(s, strSend, strSend.GetLength() + 1, 0,
						(SOCKADDR *) &addrFrom, sizeof(SOCKADDR));
				}
			}
			else if (strRecv.Left(4) == _T("REG_"))
			{
				CString strKey = strRecv.Right(strRecv.GetLength() - 4);
				MAKE_CODE(strKey);
				CString strActive = MD5String(strKey);
				MAKE_CODE(strActive);
				strActive = _T("ACT_") + strActive;
				sendto(s, strActive, strActive.GetLength() + 1, 0,
						(SOCKADDR *) &addrFrom, sizeof(SOCKADDR));
			}
			else if (strRecv.Left(4) == _T("ACT_"))
			{
				m_strActive = strRecv.Right(strRecv.GetLength() - 4);
				InterlockedExchange(&m_lRegistered, 1L);
				break;
			}
		}
	}

	// TRACE(_T("Recv Thread End\n"));
}

BOOL CIPConfig::StartServer(LPCTSTR pszServer, u_short uPort /* = 28001 */)
{
	if (m_hThread != NULL)
	{
		return FALSE;
	}
	
	m_strServer = pszServer;
	m_strServer.MakeUpper();

	GetBroadcastIP();

// 	SOCKADDR_IN addrSend;
// 	// addrSend.sin_addr.S_un.S_addr = inet_addr(m_IPbc);
// 	addrSend.sin_addr.s_addr = INADDR_BROADCAST;
// 	addrSend.sin_family = AF_INET;
// 	addrSend.sin_port = htons(28000);
// 	
// 	BOOL b = 1;
// 	int nResult = setsockopt(m_socket, SOL_SOCKET, SO_BROADCAST, (char*)&b, sizeof(BOOL));
// 
// 	CString strSend;
// 	strSend.Format(_T("IP_%s:%s"), m_strServer, m_strIP);
// 	
// 	if (sendto(m_socket, strSend, strSend.GetLength() + 1, 0,
// 		(SOCKADDR *) &addrSend, sizeof(SOCKADDR)) < 0)
// 	{
// 		TRACE(_T("Board cast failed."));
// 	}

	SOCKADDR_IN addrServ;
	addrServ.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrServ.sin_family = AF_INET;
	addrServ.sin_port = htons(uPort);
	if (bind(m_socket, (SOCKADDR *) &addrServ, sizeof(SOCKADDR)) < 0)
	{
		AfxMessageBox(_T("Bind failed."));
		return FALSE;
	}


	m_s = m_socket;
	InterlockedExchange(&m_lThread, 1L);
	m_hThread = CreateThread(NULL, 0, RecvReqThread, this, 0, NULL);
	while (m_lThread)
	{
		Sleep(100);
	}

	uPort++;
	addrServ.sin_port = htons(uPort);
	if (bind(m_socketR, (SOCKADDR *) &addrServ, sizeof(SOCKADDR)) < 0)
	{
		AfxMessageBox(_T("Bind failed."));
		return FALSE;
	}

	m_s = m_socketR;
	InterlockedExchange(&m_lThread, 1L);
	m_hThreadR = CreateThread(NULL, 0, RecvReqThread, this, 0, NULL);
	while (m_lThread)
	{
		Sleep(100);
	}

	return TRUE;
}

void CIPConfig::GetAllServers()
{
	SOCKADDR_IN addrSend;
	// addrSend.sin_addr.S_un.S_addr = inet_addr(m_IPbc);
	addrSend.sin_addr.s_addr = INADDR_BROADCAST;
	addrSend.sin_family = AF_INET;

	BOOL b = 1;
	int nResult = setsockopt(m_socket, SOL_SOCKET, SO_BROADCAST, (char*)&b, sizeof(BOOL));
	
	m_lstServers.RemoveAll();
	m_lstServers.AddTail(_T("RegServer"));

	CString strSend;
	u_short uPort = 28001;
	for (POSITION pos = m_lstServers.GetHeadPosition(); pos != NULL;)
	{
		CString strServer =  m_lstServers.GetNext(pos);
		WritePrivateProfileString(strServer, _T("IPA"), _T("127.0.0.1"), m_strFile);
		strSend.Format(_T("GET_%s"), strServer);
		addrSend.sin_port = htons(uPort++);
		if (sendto(m_socket, strSend, strSend.GetLength() + 1, 0,
			(SOCKADDR *) &addrSend, sizeof(SOCKADDR)) < 0)
		{
			TRACE(_T("Board cast failed."));
		}
	}

	if (m_hThread == NULL)
	{
		m_s = m_socket;
		InterlockedExchange(&m_lThread, 1L);
		m_hThread = CreateThread(NULL, 0, RecvReqThread, this, 0, NULL);
		while (m_lThread)
		{
			Sleep(100);
		}
	}
}

CString CIPConfig::GetServerIP(LPCTSTR pszServer)
{
	TCHAR szBuffer[MAX_PATH];
	GetPrivateProfileString(pszServer, _T("Enable"), _T(""), szBuffer, MAX_PATH, m_strFile);
	if (_ttoi(szBuffer) == 0)
	{
		GetPrivateProfileString(pszServer, _T("IPA"), _T("127.0.0.1"), szBuffer, MAX_PATH, m_strFile);
		return CString(szBuffer);
	}
	GetPrivateProfileString(pszServer, _T("IP"), _T(""), szBuffer, MAX_PATH, m_strFile);
	CString strIP = szBuffer;
	if (strIP.IsEmpty())
	{
		GetPrivateProfileString(pszServer, _T("IPA"), _T("127.0.0.1"), szBuffer, MAX_PATH, m_strFile);
		return CString(szBuffer);
	}
	return strIP;
}

UINT CIPConfig::GetServerPort(LPCTSTR pszServer, UINT nDefault /* = 27000 */)
{
	TCHAR szBuffer[MAX_PATH];
	GetPrivateProfileString(pszServer, _T("Enable"), _T(""), szBuffer, MAX_PATH, m_strFile);
	if (_ttoi(szBuffer) == 0)
	{
		return nDefault;
	}

	GetPrivateProfileString(pszServer, _T("Port"), _T(""), szBuffer, MAX_PATH, m_strFile);
	return _ttoi(szBuffer);
}

CString CIPConfig::RegToServer(LPCTSTR pszServer, LPCTSTR pszKey, long lTimeOut /* = 5 */)
{
	SOCKADDR_IN addrRecv;

	addrRecv.sin_family = AF_INET;
	addrRecv.sin_addr.s_addr = htons(INADDR_ANY);
	addrRecv.sin_port = htons(0);

	if (bind(m_socketR, (SOCKADDR *)&addrRecv, sizeof(addrRecv)))
	{
		AfxMessageBox(_T("Bind failed."));
	}

	m_s = m_socketR;
	InterlockedExchange(&m_lThread, 1L);
	HANDLE hThread = CreateThread(NULL, 0, RecvReqThread, this, 0, NULL);
	while (m_lThread)
	{
		Sleep(100);
	}
	//CloseHandle(hThread);
	
	CString strActive;

	SOCKADDR_IN addrSend;
	addrSend.sin_addr.S_un.S_addr = inet_addr(GetServerIP(pszServer));
	addrSend.sin_family = AF_INET;

	CString strSend;
	u_short uPort = 28002;

	strSend.Format(_T("REG_%s"), pszKey);
	addrSend.sin_port = htons(uPort);

	InterlockedExchange(&m_lRegistered, 0L);

	if (sendto(m_socketR, strSend, strSend.GetLength() + 1, 0,
		(SOCKADDR *) &addrSend, sizeof(SOCKADDR)) < 0)
	{
		TRACE(_T("Send key failed."));
	}

	// Max 10 minutes time out
	if ((lTimeOut < 0)||(lTimeOut > 600))
		lTimeOut = 600;

	while (lTimeOut)
	{
		if (m_lRegistered)
			return m_strActive;
		Sleep(500);
		if (m_lRegistered)
			return m_strActive;
		Sleep(500);
		lTimeOut--;
	}

	return strActive;
}